package com.zzyl.nursing.service.impl;

import java.awt.geom.Arc2D;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.json.JSONUtil;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.constant.Constants;
import com.zzyl.common.utils.DateUtils;
import com.zzyl.nursing.domain.AlertData;
import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.service.IAlertDataService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Service;
import com.zzyl.nursing.mapper.AlertRuleMapper;
import com.zzyl.nursing.domain.AlertRule;
import com.zzyl.nursing.service.IAlertRuleService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.util.CollectionUtils;

/**
 * 报警规则Service业务层处理
 * 
 * @author xuanzhi
 * @date 2025-09-24
 */
@Service
public class AlertRuleServiceImpl extends ServiceImpl<AlertRuleMapper, AlertRule> implements IAlertRuleService
{
    @Autowired
    private AlertRuleMapper alertRuleMapper;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired
    private IAlertDataService alertDataService;
    /**
     * 查询报警规则
     * 
     * @param id 报警规则主键
     * @return 报警规则
     */
    @Override
    public AlertRule selectAlertRuleById(Long id)
    {
        return alertRuleMapper.selectById(id);
    }

    /**
     * 查询报警规则列表
     * 
     * @param alertRule 报警规则
     * @return 报警规则
     */
    @Override
    public List<AlertRule> selectAlertRuleList(AlertRule alertRule)
    {
        return alertRuleMapper.selectAlertRuleList(alertRule);
    }

    /**
     * 新增报警规则
     * 
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int insertAlertRule(AlertRule alertRule)
    {
        return alertRuleMapper.insert(alertRule);
    }

    /**
     * 修改报警规则
     * 
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int updateAlertRule(AlertRule alertRule)
    {
        return alertRuleMapper.updateById(alertRule);
    }

    /**
     * 批量删除报警规则
     * 
     * @param ids 需要删除的报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleByIds(Long[] ids)
    {
        return alertRuleMapper.deleteBatchIds(Arrays.asList(ids));
    }

    /**
     * 删除报警规则信息
     * 
     * @param id 报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleById(Long id)
    {
        return alertRuleMapper.deleteById(id);
    }

    /**
     * 实现报警信息传递
     * 本次代码基于讲义流程图来写,分成四部分
     * 本次redis中只存入了手表数据进行测试
     */
    @Override
    public void alert() {
        /**
         * 第一部分:效验
         */
        //1.查询所有的启用规则
        List<AlertRule> list = lambdaQuery().eq(AlertRule::getStatus, 1).list();
        if(CollectionUtils.isEmpty(list)){
            return;//如果查不到正在启用规则的数据,直接进行返回
        }
        //2.从redis中查询上报的数据(需要取出全部的值,因此不能用get)
        List values = redisTemplate.opsForHash().values(Constants.IOT_DEVICE_LAST_DATA);
        List<DeviceData> list1=new ArrayList<>();
        for (Object value : values) {
            List<DeviceData> data = JSONUtil.toList(value.toString(), DeviceData.class);//将各个value的值转成list集合
            list1.addAll(data);//将values的值全部添加进集合
        }
        /**
         * 第二部分:对设备数据和规则进行匹配
         */
        for (DeviceData device : list1) {
            for (AlertRule rule : list) {
                //1.判断数据上报的时间是否大于1分钟
                LocalDateTime alarmTime = device.getAlarmTime().plusMinutes(1);//将上传时间加上一分钟
                LocalDateTime now = LocalDateTime.now();//获取当前时间
                if(alarmTime.isBefore(now)){//如果数据上报的时间加上一分钟还是在当前时间的前面
                    continue;//进行下一个判断
                }
                //2.匹配productkey和functionid字段
                if (!device.getProductKey().equals(rule.getProductKey())) {
                    continue;
                }
                if(!device.getFunctionId().equals(rule.getFunctionId())){
                    continue;
                }
                //3.判断规则是否是关联全部设备还是设备号互相匹配
                if("-1".equals(rule.getIotId())||rule.getIotId().equals(device.getIotId())){
                    //正式进入匹配,调用方法,分层解耦
                    match(device,rule);
                }

            }
        }
    }

    /**
     * 第三部分:规则效验,核心功能,此时device,rule已经匹配上了,手牵着手进入这个方法
     * @param device
     * @param rule
     */
    private void match(DeviceData device,AlertRule rule){
        //1.在上报时间范围内吗?
        LocalTime alarmTime = device.getAlarmTime().toLocalTime();
        String[] split = rule.getAlertEffectivePeriod().split("~");
        LocalTime start = LocalTime.parse(split[0], DateTimeFormatter.ofPattern("HH:mm:ss"));
        LocalTime end = LocalTime.parse(split[1], DateTimeFormatter.ofPattern("HH:mm:ss"));
        if(alarmTime.isBefore(start)|| alarmTime.isAfter(end)){
            return;
        }
        //2.上报数据符合阈值条件吗?
        String key =CacheConstants.ALERT_COUNT_KEY+":"+rule.getIotId()+":"+rule.getId()+":"+device.getFunctionId(); //加上这些信息使储存在redis中的数据更加清晰
        String operator = rule.getOperator();
        Double value = rule.getValue();
        if(">=".equals(operator)&&value>Double.parseDouble(device.getDataValue())){
            redisTemplate.delete(key);
            return;
        }
        if("<".equals(operator)&&value<=Double.parseDouble(device.getDataValue())){
            redisTemplate.delete(key);
            return;
        }
        //3.上报数据符合持续周期吗?(核心代码)
        Long increment = redisTemplate.opsForValue().increment(key);
        if(increment.intValue()!=rule.getDuration()){
            return;
        }
        //4.上报数据符合沉默周期吗?(核心代码)
        String silent=CacheConstants.ALERT_SILENT_KEY+":"+rule.getIotId()+":"+rule.getId()+":"+device.getFunctionId(); //加上这些信息使储存在redis中的数据更加清晰
        if(redisTemplate.hasKey(silent)){//判断一个值是否存在
            return;
        }
        //5.持续周期清零,设置沉默周期
        redisTemplate.delete(key);//删key跑路
        redisTemplate.opsForValue().set(silent,"xuanzhi",5, TimeUnit.MINUTES);//设置沉默周期
        //6.发送警告消息,封装成一个方法,进行解耦
        message(device,rule);
    }

    /**
     * 第四部分:发送报警数据
     * @param device
     * @param rule
     */
    private void message(DeviceData device,AlertRule rule){
        Integer alertDataType = rule.getAlertDataType();
        Set<Long> userset=new HashSet<>();//使用set执行去重操作
        //1.判断报警数据类型,本次将不再写烟感设备异常的代码
        if(alertDataType==0){//老人异常数据
            if(device.getLocationType()==0){//2.手表
                List<Long> longs = deviceMapper.selectNursingIdsByIotIdWithElder(device.getIotId());
                userset.addAll(longs);
            }else{//2.床位
                List<Long> longs = deviceMapper.selectNursingIdsByIotIdWithBed(device.getIotId());
                userset.addAll(longs);
            }
        }
        //保存报警信息到数据库,继续调用方法,解耦
        insertAlertData(userset,rule,device);
    }

    /**
     * 保存报警数据
     *
     * @param allUserIds
     * @param rule
     * @param deviceData
     */
    private void insertAlertData(Collection<Long> allUserIds, AlertRule rule, DeviceData deviceData) {
        //对象拷贝
        AlertData alertData = BeanUtil.toBean(deviceData, AlertData.class);
        alertData.setAlertRuleId(rule.getId());
        //体温>=90,持续3个周期就报警
        String alertReason = CharSequenceUtil.format("{}{}{},持续{}个周期就报警", rule.getFunctionName(), rule.getOperator(), rule.getValue(), rule.getDuration());
        alertData.setAlertReason(alertReason);
        alertData.setStatus(0);
        alertData.setType(rule.getAlertDataType());
        //遍历allUserIds
        List<AlertData> list = allUserIds.stream().map(userId -> {
            AlertData dbAlertData = BeanUtil.toBean(alertData, AlertData.class);
            dbAlertData.setUserId(userId);
            dbAlertData.setId(null);
            return dbAlertData;
        }).collect(Collectors.toList());

        //批量保存
        alertDataService.saveBatch(list);

    }
}
